﻿function BoxShape(shape, origin, boxes, angle) {
    this.shape = shape;
    this.boxes = [];
    this.angle = 0;
    if (origin && boxes) {
        this.boxes = boxes;
        this.origin = origin;
        this.angle = angle;
    } else {
        switch (shape) {
            case "SQUARE":
                this.origin = { x: 6, y: 0 };
                this.boxes[0] = [1, 1];
                this.boxes[1] = [1, 1];
                break;
            case "LINE":
                this.origin = { x: 5, y: 0 };
                this.boxes[0] = [1, 1, 1, 1];
                break;
            case "LEFT-CORNER":
                this.origin = { x: 6, y: 0 };
                this.boxes[0] = [1];
                this.boxes[1] = [1, 1, 1];
                break;
            case "RIGHT-CORNER":
                this.origin = { x: 6, y: 0 };
                this.boxes[0] = [];
                this.boxes[0][2] = 1;
                this.boxes[1] = [1, 1, 1];
                break;
            case "LEFT-Z":
                this.origin = { x: 6, y: 0 };
                this.boxes[0] = [1, 1];
                this.boxes[1] = [];
                this.boxes[1][1] = 1;
                this.boxes[1][2] = 1;
                break;
            case "RIGHT-Z":
                this.origin = { x: 6, y: 0 };
                this.boxes[0] = [];
                this.boxes[0][1] = 1;
                this.boxes[0][2] = 1;
                this.boxes[1] = [1, 1];
                break;
            case "PIN":
                this.origin = { x: 6, y: 0 };
                this.boxes[0] = [];
                this.boxes[0][1] = 1;
                this.boxes[1] = [1, 1, 1];
                break;

        }
    }
}

BoxShape.prototype = {
    leftEdge: function () {
        var points = [];
        for (var i = 0; i < this.boxes.length; i++) {
            for (var j = 0; j < this.boxes[i].length; j++) {
                if (this.boxes[i][j]) {
                    points.push({ x: j, y: i });
                    break;
                }
            }
        }

        return points;
    },
    rightEdge: function () {
        var points = [];
        for (var i = 0; i < this.boxes.length; i++) {
            for (j = this.boxes[i].length - 1; j >= 0; j--) {
                if (this.boxes[i][j]) {
                    points.push({ x: j, y: i });
                }
            }
        }

        return points;
    },
    bottomEdge: function () {
        var points = [];
        var finished = false;
        for (var i = 0; finished != true; i++) {
            var isEmptyColumn = true;
            for (var j = this.boxes.length - 1; j >= 0; j--) {
                if (this.boxes[j][i]) {
                    points.push({ x: i, y: j });
                    isEmptyColumn = false;
                    break;
                }
            }

            if (isEmptyColumn) {
                finished = true;
            }
        }

        return points;
    }, topEdge: function () {
        var points = [];
        var points = [];
        var finished = false;
        for (var i = 0; finished != true; i++) {
            var isEmptyColumn = true;
            for (var j = 0; j < this.boxes.length ; j++) {
                if (this.boxes[j][i]) {
                    points.push({ x: i, y: j });
                    isEmptyColumn = false;
                    break;
                }
            }

            if (isEmptyColumn) {
                finished = true;
            }
        }

        return points;
    },
    getRotatedResult: function () {
        var newBoxes = [];
        var newOrigin;
        var angle;
        switch (this.shape) {
            case "LINE":
                switch (this.angle) {
                    case 0:
                        newBoxes[0] = [1];
                        newBoxes[1] = [1];
                        newBoxes[2] = [1];
                        newBoxes[3] = [1];

                        newOrigin = { x: this.origin.x + 1, y: this.origin.y - 1 };
                        angle = 90;
                        break;
                    case 90:
                        newBoxes[0] = [1, 1, 1, 1];
                        newOrigin = { x: this.origin.x - 2, y: this.origin.y + 1 };
                        angle = 0;
                        break;
                }
                break;
            case "LEFT-CORNER":
                switch (this.angle) {
                    case 0:
                        newBoxes[0] = [1, 1];
                        newBoxes[1] = [1];
                        newBoxes[2] = [1];

                        newOrigin = { x: this.origin.x, y: this.origin.y + 1 };
                        angle = 90;
                        break;
                    case 90:
                        newBoxes[0] = [1, 1, 1];
                        newBoxes[1] = [];
                        newBoxes[1][2] = 1;

                        newOrigin = { x: this.origin.x - 2, y: this.origin.y };
                        angle = 180;
                        break;
                    case 180:
                        newBoxes[0] = [];
                        newBoxes[0][1] = 1;
                        newBoxes[1] = [];
                        newBoxes[1][1] = 1;
                        newBoxes[2] = [1, 1];

                        newOrigin = { x: this.origin.x + 1, y: this.origin.y - 2 };
                        angle = 270;
                        break;
                    case 270:
                        newBoxes[0] = [1];
                        newBoxes[1] = [1, 1, 1];

                        newOrigin = { x: this.origin.x, y: this.origin.y - 1 };
                        angle = 0;
                        break;
                }
                break;
            case "RIGHT-CORNER":
                switch (this.angle) {
                    case 0:
                        newBoxes[0] = [1];
                        newBoxes[1] = [1];
                        newBoxes[2] = [1, 1];

                        newOrigin = { x: this.origin.x + 2, y: this.origin.y - 2 };
                        angle = 90;
                        break;
                    case 90:
                        newBoxes[0] = [1, 1, 1];
                        newBoxes[1] = [1];

                        newOrigin = { x: this.origin.x, y: this.origin.y + 2 };
                        angle = 180;
                        break;
                    case 180:
                        newBoxes[0] = [1, 1];
                        newBoxes[1] = [];
                        newBoxes[1][1] = 1;
                        newBoxes[2] = [];
                        newBoxes[2][1] = 1;

                        newOrigin = { x: this.origin.x - 1, y: this.origin.y };
                        angle = 270;
                        break;
                    case 270:
                        newBoxes[0] = [];
                        newBoxes[0][2] = 1;
                        newBoxes[1] = [1, 1, 1, ];

                        newOrigin = { x: this.origin.x - 2, y: this.origin.y - 1 };
                        angle = 0;
                        break;
                }
                break;
            case "LEFT-Z":
                switch (this.angle) {
                    case 0:
                        newBoxes[0] = [];
                        newBoxes[0][1] = 1;
                        newBoxes[1] = [1, 1];
                        newBoxes[2] = [1];
                        newOrigin = { x: this.origin.x + 1, y: this.origin.y };
                        angle = 90;
                        break;
                    case 90:
                        newBoxes[0] = [1, 1];
                        newBoxes[1] = [];
                        newBoxes[1][1] = 1;
                        newBoxes[1][2] = 1;
                        newOrigin = { x: this.origin.x - 1, y: this.origin.y + 1 };
                        angle = 0;
                        break;
                }
                break;
            case "RIGHT-Z":
                switch (this.angle) {
                    case 0:
                        newBoxes[0] = [1];
                        newBoxes[1] = [1, 1];
                        newBoxes[2] = [];
                        newBoxes[2][1] = 1;
                        newOrigin = this.origin;// { x: this.origin.x - 1, y: this.origin.y };
                        angle = 90;
                        break;
                    case 90:
                        newBoxes[0] = [];
                        newBoxes[0][1] = 1;
                        newBoxes[0][2] = 1;
                        newBoxes[1] = [1, 1];
                        newOrigin = this.origin;// { x: this.origin.x - 1, y: this.origin.y };
                        angle = 0;
                        break;
                }
                break;
            case "PIN":
                switch (this.angle) {
                    case 0:
                        newBoxes[0] = [1];
                        newBoxes[1] = [1, 1];
                        newBoxes[2] = [1];
                        newOrigin = { x: this.origin.x + 1, y: this.origin.y };
                        angle = 90;
                        break;
                    case 90:
                        newBoxes[0] = [1, 1, 1];
                        newBoxes[1] = [];
                        newBoxes[1][1] = 1;
                        newOrigin = { x: this.origin.x - 1, y: this.origin.y };
                        angle = 180;
                        break;
                    case 180:
                        newBoxes[0] = [];
                        newBoxes[0][1] = 1;
                        newBoxes[1] = [1, 1];
                        newBoxes[2] = [];
                        newBoxes[2][1] = 1;

                        newOrigin = { x: this.origin.x, y: this.origin.y - 1 };
                        angle = 270;
                        break;
                    case 270:
                        newBoxes[0] = [];
                        newBoxes[0][1] = 1;
                        newBoxes[1] = [1, 1, 1];
                        newOrigin = this.origin;
                        angle = 0;
                        break;
                }
                break;
        }

        return new BoxShape(this.shape, newOrigin, newBoxes, angle);
    }

};

function buildUserGame(gameContainerId, user, socket, acceptInput) {
    var boxContainer;
    var rows = [];
    var maxRowIndex = 23;
    var maxColumnIndex = 14;
    var currentShape;
    var timer;
    var interval = 200;
    var shapes = ["SQUARE", "LINE", "LEFT-CORNER", "RIGHT-CORNER", "LEFT-Z", "RIGHT-Z", "PIN"];
    var levelLabel;
    var scoreLabel;
    var score = 0;
    var nextShape;
    var nextShapeBox;

    gameContainerId = "#" + gameContainerId;

    function getCell(x, y) {
        return rows[x][y];
    }

    function pushEmptyRow() {
        boxContainer.prepend(buildRowHtml());
    }

    function eraseRow(x) {
        $(gameContainerId + " .box-container .row:eq(" + x + ")").remove();
        rows.splice(x, 1);
    }

    function down() {
        if (!isStop()) {
            eraseShape();
            currentShape.origin.y++;
            showShape(currentShape);

            if (isStop()) {
                fixShape();
                checkAndErase();
            } else {
                if (timer) {
                    clearTimeout(timer);
                }

                timer = setTimeout(down, interval);

            }
        } else {
            fixShape();
            checkAndErase();
        }
    }

    function showShape(shape) {
        var boxes = shape.boxes;
        for (var i = 0; i < boxes.length; i++) {
            for (var j = 0; j < boxes[i].length; j++) {
                if (boxes[i][j]) {
                    $(getCell(shape.origin.y + i, shape.origin.x + j)).addClass("moving");
                }
            }
        }

        socket.send(JSON.stringify({ command: "GA", data: "Display", extendData: { data: collectBoxData(), score: score } }));
    }

    function fixShape() {
        if (currentShape) {
            var boxes = currentShape.boxes;
            for (var i = 0; i < boxes.length; i++) {
                for (var j = 0; j < boxes[i].length; j++) {
                    if (boxes[i][j]) {
                        $(getCell(currentShape.origin.y + i, currentShape.origin.x + j)).removeClass("moving")                          
                            .addClass("filled");
                    }
                }
            }
        }
    }

    function canMovetoLeft() {
        var leftEdge = currentShape.leftEdge();
        for (var i = 0; i < leftEdge.length; i++) {
            if (currentShape.origin.x + leftEdge[i].x == 0 ||
               isBoxFilled(currentShape.origin.y + leftEdge[i].y, currentShape.origin.x + leftEdge[i].x - 1)) {
                return false;
            }
        }

        return true;
    }

    function canMovetoRight() {
        var rightEdge = currentShape.rightEdge();
        for (var i = 0; i < rightEdge.length; i++) {
            if (currentShape.origin.x + rightEdge[i].x == maxColumnIndex ||
               isBoxFilled(currentShape.origin.y + rightEdge[i].y, currentShape.origin.x + rightEdge[i].x + 1)) {
                return false;
            }
        }

        return true;
    }

    function movetoRight(propagate) {
        propagate = propagate || true;
        if (currentShape) {
            if (canMovetoRight()) {
                eraseShape();
                currentShape.origin.x++;
                showShape(currentShape);
                console.log("move to right");
            }
        }
    }

    function movetoLeft(propagate) {
        propagate = propagate || true;
        if (currentShape) {
            if (canMovetoLeft()) {
                eraseShape();
                currentShape.origin.x--;
                showShape(currentShape);
                console.log("move to left");
            }
        }
    }

    function moveDown() {
        if (isStop()) {
            currentShape = null;
            //newShape();
        } else {
            eraseShape();
            currentShape.origin.y++;
            showShape(currentShape);
            console.log("move down");
        }
    }

    function eraseShape() {
        if (currentShape) {
            var boxes = currentShape.boxes;
            for (var i = 0; i < boxes.length; i++) {
                for (var j = 0; j < boxes[i].length; j++) {
                    if (boxes[i][j]) {
                        $(getCell(currentShape.origin.y + i, currentShape.origin.x + j)).removeClass("filled moving");
                    }
                }
            }
        }
    }

    function rotate(propagate) {
        propagate = propagate || true;
        if (currentShape && currentShape.shape != "SQUARE") {
            var rotatedShape = currentShape.getRotatedResult();

            var topEdgePoints = rotatedShape.topEdge();
            for (var i = 0; i < topEdgePoints.length; i++) {
                if (rotatedShape.origin.y + topEdgePoints[i].y < 0 ||
                    isBoxFilled(rotatedShape.origin.y + topEdgePoints[i].y, rotatedShape.origin.x + topEdgePoints[i].x)) {
                    return;
                }
            }

            var leftEdgePoints = rotatedShape.leftEdge();
            for (var i = 0; i < leftEdgePoints.length; i++) {
                if (rotatedShape.origin.x + leftEdgePoints[i].x < 0 ||
                    isBoxFilled(rotatedShape.origin.y + leftEdgePoints[i].y, rotatedShape.origin.x + leftEdgePoints[i].x)) {
                    return;
                }
            }

            var rightEdgePoints = rotatedShape.rightEdge();
            for (var i = 0; i < rightEdgePoints.length; i++) {
                if (rotatedShape.origin.x + rightEdgePoints[i].x > maxColumnIndex ||
                    isBoxFilled(rotatedShape.origin.y + rightEdgePoints[i].y, rotatedShape.origin.x + rightEdgePoints[i].x)) {
                    return;
                }
            }

            var bottomEdgePoints = rotatedShape.bottomEdge();
            for (var i = 0; i < bottomEdgePoints.length; i++) {
                if (rotatedShape.origin.y + bottomEdgePoints[i].y > maxRowIndex ||
                   isBoxFilled(rotatedShape.origin.y + bottomEdgePoints[i].y, rotatedShape.origin.x + bottomEdgePoints[i].x)) {
                    return;
                }
            }

            eraseShape();
            currentShape = rotatedShape;
            showShape(currentShape);
        }
    }

    function isStop() {
        if (currentShape) {
            if ((currentShape.origin.y + currentShape.boxes.length) == maxRowIndex) {
                return true;
            } else {
                var edgePoints = currentShape.bottomEdge();
                for (var i = 0; i < edgePoints.length; i++) {
                    var point = edgePoints[i];
                    if (isBoxFilled(currentShape.origin.y + point.y + 1, currentShape.origin.x + point.x)) {
                        return true;
                    }
                }
            }
        } else {
            return true;
        }

        return false;
    }

    function isFailed() {
        if (isStop()) {
            return true;
        }

        var boxes = currentShape.boxes;
        for (var i = 0; i < boxes.length; i++) {
            for (var j = 0; j < boxes[i].length; j++) {
                if (boxes[i][j]) {
                    if (isBoxFilled(currentShape.origin.y + j, currentShape.origin.x + i)) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    function newShape() {
        var shapeString;
        if (nextShape) {
            shapeString = nextShape;
        } else {
            shapeString = shapes[Math.floor(Math.random() * 10) % shapes.length];
        }

        nextShape = shapes[Math.floor(Math.random() * 10) % shapes.length];
        drawNextShape();
        currentShape = new BoxShape(shapeString); //new BoxShape("SQUARE");    
        if (isFailed()) {
            alert("The game is over!You failed!");
        } else {
            down();
        }

    }

    function drawNextShape() {
        $(gameContainerId + " .shape-box .cell").removeClass("filled");
        var shape = new BoxShape(nextShape, { x: 0, y: 0 }, 0);
        for (var i = 0; i < shape.boxes.length; i++) {
            var row = shape.boxes[i];
            for (var j = 0; j < row.length; j++) {
                if (row[j]) {
                    $(gameContainerId + " .shape-box .row:eq(" + i + ") .cell:eq(" + j + ")").addClass("moving");
                }
            }
        }
    }

    function isBoxFilled(x, y) {
        if ($(getCell(x, y)).hasClass("filled")) {
            return true;
        }

        return false;
    }

    function checkAndErase() {
        if (currentShape) {
            var rowCount = currentShape.boxes.length;
            var startRowIndex = currentShape.origin.y;
            var rowsForDelete = [];
            for (var i = rowCount - 1; i >= 0; i--) {
                if (rows[startRowIndex + i].every(function (el) {
                    return $(el).hasClass("filled");
                })) {
                    rowsForDelete.push(startRowIndex + i);
                }
            }

            if (rowsForDelete.length > 0) {
                score += 10 * rowsForDelete.length * rowsForDelete.length;
                updateScore(score);
                eraseRows(rowsForDelete, 0);
            } else {
                currentShape = null;
                newShape();
            }
        }
    }

    function updateScore(x) {
        scoreLabel.text(x);
    }

    function eraseRows(deleteRows, times) {
        if (times == 6) {
            for (var i = 0; i < deleteRows.length; i++) {
                eraseRow(deleteRows[i]);
            }

            for (var i = 0; i < deleteRows.length; i++) {
                pushEmptyRow();
                rows.unshift($(gameContainerId + " .box-container .row:eq(0)").children().toArray());
            }

            currentShape = null;
            newShape();
            socket.send(JSON.stringify({ command: "GA", data: "Display", extendData: { data: collectBoxData(), score: score } }));
        } else {
            for (var i = 0; i < deleteRows.length; i++) {
                $(gameContainerId + " .box-container .row:eq(" + deleteRows[i] + ") .cell").toggleClass("filled");
            }

            setTimeout(eraseRows, 100, deleteRows, ++times);
        }
    }

    function buildRowHtml() {
        var rowHtml = "<div class=\"row\">";
        for (var j = 0; j <= maxColumnIndex; j++) {
            rowHtml += "<div class=\"cell\"></div>"
        }
        rowHtml += "</div>";

        return rowHtml;
    }

    function downDirectly() {
        if (timer) {
            clearTimeout(timer);
        }
        eraseShape();
        while (!isStop()) {
            currentShape.origin.y++;
        }
        //showShape(currentShape);
        fixShape();
        checkAndErase();
    }

    var gameHtml = '<div class="game-header"><img src="portrait.gif"/><div class="user-name-box">' + user + '</div></div>' +
                                       '<div class="info-panel">' +
                                           '<div class="info-detail-panel">' +
                                           'LEVEL:<span class="level-label number-info">1</span>SCORE:<span class="score-label number-info">0</span>' +
                                   '</div>' +
                                   '<div class="next-box-panel">' +
                                       '<div class="shape-box">' +
                                           '<div class="row">' +
                                               '<div class="cellsample"></div>' +
                                               '<div class="cellsample"></div>' +
                                               '<div class="cellsample"></div>' +
                                               '<div class="cellsample"></div>' +
                                           '</div>' +
                                           '<div class="row">' +
                                               '<div class="cellsample"></div>' +
                                               '<div class="cellsample"></div>' +
                                               '<div class="cellsample"></div>' +
                                               '<div class="cellsample"></div>' +
                                           '</div>' +
                                       '</div>' +
                                   '</div>' +
                               '</div>' +
                               '<div class="box-container">' +
                               '</div>';

    $(gameContainerId).html(gameHtml);

    boxContainer = $(gameContainerId + " .box-container");

    boxContainer.css("width", (maxColumnIndex + 1) * 30);
    for (var i = 0; i < maxRowIndex; i++) {
        boxContainer.append(buildRowHtml());
    }

    $(gameContainerId + " .box-container .row").each(function (index) {
        rows[index] = $(this).children().toArray();
    });

    nextShapeBox = $(gameContainerId + " .shape-box");

    levelLabel = $(gameContainerId + " .level-label");
    scoreLabel = $(gameContainerId + " .score-label");

    if (acceptInput) {
        window.onkeydown = function (event) {
            event = event || window.event;
            console.log(event.keyCode);
            switch (event.keyCode) {
                //move to right
                case 39:
                case 68:
                    movetoRight();
                    break;
                    //move to left
                case 37:
                case 65:
                    movetoLeft();
                    break;
                case 40:
                case 83:
                    //moveDown();
                    break;
                    //rotate
                case 81:
                case 38:
                    rotate();
                    break;
                case 90:
                    downDirectly();
                    break;
            }
        }
    }

    function displayBoxes(boxData) {
        for (var i = 0; i < boxData.data.length; i++) {
            for (var j = 0; j < boxData.data[i].length; j++)
                if (boxData.data[i][j] == 0) {
                    $(getCell(i, j)).removeClass("filled moving");
                } else if (boxData.data[i][j] == 1) {
                    $(getCell(i, j)).removeClass("filled").addClass("moving");
                } else {
                    $(getCell(i, j)).removeClass("moving").addClass("filled");
                }
        }

        updateScore(boxData.score);
    }




    function collectBoxData() {
        var data = [];
        for (var i = 0; i < rows.length; i++) {
            var rowdata = [];
            for (var j = 0; j < rows[i].length; j++) {
                if ($(getCell(i, j)).hasClass("moving")) {
                    rowdata[j] = 1;
                } else if ($(getCell(i, j)).hasClass("filled")) {
                    rowdata[j] = 2;
                } else {
                    rowdata[j] = 0;
                }
            }

            data.push(rowdata);
        }
        return data;
    }

    $(gameContainerId).css("display", "block");

    return {
        start: function () {
            newShape();
        },
        processCommand: function (command) {
            switch (command.data) {
                case "Display":
                    displayBoxes(command.extendData);
                    break;
                    //case "MoveToRight":
                    //    movetoRight(false);
                    //    break;
                    //case "MoveToLeft":
                    //    movetoLeft(false);
                    //    break;
                    //case "Rotate":
                    //    rotate(false);
                    //    break;
                    //case "Down":
                    //    moveDown(false);
                    //    break;
                    //case "NewShape":
                    //    currentShape = new BoxShape(command.extendData);
                    //    beak;
            }
        },
        containId: gameContainerId,
        pause: function () {
        }
    }
};